home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / files.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-13  |  18.0 KB  |  881 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20.  
  21. #include "qcommon.h"
  22.  
  23. // define this to dissalow any data but the demo pak file
  24. //#define    NO_ADDONS
  25.  
  26. // if a packfile directory differs from this, it is assumed to be hacked
  27. // Full version
  28. #define    PAK0_CHECKSUM    0x40e614e0
  29. // Demo
  30. //#define    PAK0_CHECKSUM    0xb2c6d7ea
  31. // OEM
  32. //#define    PAK0_CHECKSUM    0x78e135c
  33.  
  34. /*
  35. =============================================================================
  36.  
  37. QUAKE FILESYSTEM
  38.  
  39. =============================================================================
  40. */
  41.  
  42.  
  43. //
  44. // in memory
  45. //
  46.  
  47. typedef struct
  48. {
  49.     char    name[MAX_QPATH];
  50.     int        filepos, filelen;
  51. } packfile_t;
  52.  
  53. typedef struct pack_s
  54. {
  55.     char    filename[MAX_OSPATH];
  56.     FILE    *handle;
  57.     int        numfiles;
  58.     packfile_t    *files;
  59. } pack_t;
  60.  
  61. char    fs_gamedir[MAX_OSPATH];
  62. cvar_t    *fs_basedir;
  63. cvar_t    *fs_cddir;
  64. cvar_t    *fs_gamedirvar;
  65.  
  66. typedef struct filelink_s
  67. {
  68.     struct filelink_s    *next;
  69.     char    *from;
  70.     int        fromlength;
  71.     char    *to;
  72. } filelink_t;
  73.  
  74. filelink_t    *fs_links;
  75.  
  76. typedef struct searchpath_s
  77. {
  78.     char    filename[MAX_OSPATH];
  79.     pack_t    *pack;        // only one of filename / pack will be used
  80.     struct searchpath_s *next;
  81. } searchpath_t;
  82.  
  83. searchpath_t    *fs_searchpaths;
  84. searchpath_t    *fs_base_searchpaths;    // without gamedirs
  85.  
  86.  
  87. /*
  88.  
  89. All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.
  90.  
  91. The "base directory" is the path to the directory holding the quake.exe and all game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.  This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory.  The base directory is
  92. only used during filesystem initialization.
  93.  
  94. The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to.  This can be overridden with the "-game" command line parameter.  The game directory can never be changed while quake is executing.  This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
  95.  
  96. */
  97.  
  98.  
  99. /*
  100. ================
  101. FS_filelength
  102. ================
  103. */
  104. int FS_filelength (FILE *f)
  105. {
  106.     int        pos;
  107.     int        end;
  108.  
  109.     pos = ftell (f);
  110.     fseek (f, 0, SEEK_END);
  111.     end = ftell (f);
  112.     fseek (f, pos, SEEK_SET);
  113.  
  114.     return end;
  115. }
  116.  
  117.  
  118. /*
  119. ============
  120. FS_CreatePath
  121.  
  122. Creates any directories needed to store the given filename
  123. ============
  124. */
  125. void    FS_CreatePath (char *path)
  126. {
  127.     char    *ofs;
  128.     
  129.     for (ofs = path+1 ; *ofs ; ofs++)
  130.     {
  131.         if (*ofs == '/')
  132.         {    // create the directory
  133.             *ofs = 0;
  134.             Sys_Mkdir (path);
  135.             *ofs = '/';
  136.         }
  137.     }
  138. }
  139.  
  140.  
  141. /*
  142. ==============
  143. FS_FCloseFile
  144.  
  145. For some reason, other dll's can't just cal fclose()
  146. on files returned by FS_FOpenFile...
  147. ==============
  148. */
  149. void FS_FCloseFile (FILE *f)
  150. {
  151.     fclose (f);
  152. }
  153.  
  154.  
  155. // RAFAEL
  156. /*
  157.     Developer_searchpath
  158. */
  159. int    Developer_searchpath (int who)
  160. {
  161.     
  162.     int        ch;
  163.     // PMM - warning removal
  164. //    char    *start;
  165.     searchpath_t    *search;
  166.     
  167.     if (who == 1) // xatrix
  168.         ch = 'x';
  169.     else if (who == 2)
  170.         ch = 'r';
  171.  
  172.     for (search = fs_searchpaths ; search ; search = search->next)
  173.     {
  174.         if (strstr (search->filename, "xatrix"))
  175.             return 1;
  176.  
  177.         if (strstr (search->filename, "rogue"))
  178.             return 2;
  179. /*
  180.         start = strchr (search->filename, ch);
  181.  
  182.         if (start == NULL)
  183.             continue;
  184.  
  185.         if (strcmp (start ,"xatrix") == 0)
  186.             return (1);
  187. */
  188.     }
  189.     return (0);
  190.  
  191. }
  192.  
  193.  
  194. /*
  195. ===========
  196. FS_FOpenFile
  197.  
  198. Finds the file in the search path.
  199. returns filesize and an open FILE *
  200. Used for streaming data out of either a pak file or
  201. a seperate file.
  202. ===========
  203. */
  204. int file_from_pak = 0;
  205. #ifndef NO_ADDONS
  206. int FS_FOpenFile (char *filename, FILE **file)
  207. {
  208.     searchpath_t    *search;
  209.     char            netpath[MAX_OSPATH];
  210.     pack_t            *pak;
  211.     int                i;
  212.     filelink_t        *link;
  213.  
  214.     file_from_pak = 0;
  215.  
  216.     // check for links first
  217.     for (link = fs_links ; link ; link=link->next)
  218.     {
  219.         if (!strncmp (filename, link->from, link->fromlength))
  220.         {
  221.             Com_sprintf (netpath, sizeof(netpath), "%s%s",link->to, filename+link->fromlength);
  222.             *file = fopen (netpath, "rb");
  223.             if (*file)
  224.             {        
  225.                 Com_DPrintf ("link file: %s\n",netpath);
  226.                 return FS_filelength (*file);
  227.             }
  228.             return -1;
  229.         }
  230.     }
  231.  
  232. //
  233. // search through the path, one element at a time
  234. //
  235.     for (search = fs_searchpaths ; search ; search = search->next)
  236.     {
  237.     // is the element a pak file?
  238.         if (search->pack)
  239.         {
  240.         // look through all the pak file elements
  241.             pak = search->pack;
  242.             for (i=0 ; i<pak->numfiles ; i++)
  243.                 if (!Q_strcasecmp (pak->files[i].name, filename))
  244.                 {    // found it!
  245.                     file_from_pak = 1;
  246.                     Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename);
  247.                 // open a new file on the pakfile
  248.                     *file = fopen (pak->filename, "rb");
  249.                     if (!*file)
  250.                         Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename);    
  251.                     fseek (*file, pak->files[i].filepos, SEEK_SET);
  252.                     return pak->files[i].filelen;
  253.                 }
  254.         }
  255.         else
  256.         {        
  257.     // check a file in the directory tree
  258.             
  259.             Com_sprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename);
  260.             
  261.             *file = fopen (netpath, "rb");
  262.             if (!*file)
  263.                 continue;
  264.             
  265.             Com_DPrintf ("FindFile: %s\n",netpath);
  266.  
  267.             return FS_filelength (*file);
  268.         }
  269.         
  270.     }
  271.     
  272.     Com_DPrintf ("FindFile: can't find %s\n", filename);
  273.     
  274.     *file = NULL;
  275.     return -1;
  276. }
  277.  
  278. #else
  279.  
  280. // this is just for demos to prevent add on hacking
  281.  
  282. int FS_FOpenFile (char *filename, FILE **file)
  283. {
  284.     searchpath_t    *search;
  285.     char            netpath[MAX_OSPATH];
  286.     pack_t            *pak;
  287.     int                i;
  288.  
  289.     file_from_pak = 0;
  290.  
  291.     // get config from directory, everything else from pak
  292.     if (!strcmp(filename, "config.cfg") || !strncmp(filename, "players/", 8))
  293.     {
  294.         Com_sprintf (netpath, sizeof(netpath), "%s/%s",FS_Gamedir(), filename);
  295.         
  296.         *file = fopen (netpath, "rb");
  297.         if (!*file)
  298.             return -1;
  299.         
  300.         Com_DPrintf ("FindFile: %s\n",netpath);
  301.  
  302.         return FS_filelength (*file);
  303.     }
  304.  
  305.     for (search = fs_searchpaths ; search ; search = search->next)
  306.         if (search->pack)
  307.             break;
  308.     if (!search)
  309.     {
  310.         *file = NULL;
  311.         return -1;
  312.     }
  313.  
  314.     pak = search->pack;
  315.     for (i=0 ; i<pak->numfiles ; i++)
  316.         if (!Q_strcasecmp (pak->files[i].name, filename))
  317.         {    // found it!
  318.             file_from_pak = 1;
  319.             Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename);
  320.         // open a new file on the pakfile
  321.             *file = fopen (pak->filename, "rb");
  322.             if (!*file)
  323.                 Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename);    
  324.             fseek (*file, pak->files[i].filepos, SEEK_SET);
  325.             return pak->files[i].filelen;
  326.         }
  327.     
  328.     Com_DPrintf ("FindFile: can't find %s\n", filename);
  329.     
  330.     *file = NULL;
  331.     return -1;
  332. }
  333.  
  334. #endif
  335.  
  336.  
  337. /*
  338. =================
  339. FS_ReadFile
  340.  
  341. Properly handles partial reads
  342. =================
  343. */
  344. void CDAudio_Stop(void);
  345. #define    MAX_READ    0x10000        // read in blocks of 64k
  346. void FS_Read (void *buffer, int len, FILE *f)
  347. {
  348.     int        block, remaining;
  349.     int        read;
  350.     byte    *buf;
  351.     int        tries;
  352.  
  353.     buf = (byte *)buffer;
  354.  
  355.     // read in chunks for progress bar
  356.     remaining = len;
  357.     tries = 0;
  358.     while (remaining)
  359.     {
  360.         block = remaining;
  361.         if (block > MAX_READ)
  362.             block = MAX_READ;
  363.         read = fread (buf, 1, block, f);
  364.         if (read == 0)
  365.         {
  366.             // we might have been trying to read from a CD
  367.             if (!tries)
  368.             {
  369.                 tries = 1;
  370.                 CDAudio_Stop();
  371.             }
  372.             else
  373.                 Com_Error (ERR_FATAL, "FS_Read: 0 bytes read");
  374.         }
  375.  
  376.         if (read == -1)
  377.             Com_Error (ERR_FATAL, "FS_Read: -1 bytes read");
  378.  
  379.         // do some progress bar thing here...
  380.  
  381.         remaining -= read;
  382.         buf += read;
  383.     }
  384. }
  385.  
  386. /*
  387. ============
  388. FS_LoadFile
  389.  
  390. Filename are reletive to the quake search path
  391. a null buffer will just return the file length without loading
  392. ============
  393. */
  394. int FS_LoadFile (char *path, void **buffer)
  395. {
  396.     FILE    *h;
  397.     byte    *buf;
  398.     int        len;
  399.  
  400.     buf = NULL;    // quiet compiler warning
  401.  
  402. // look for it in the filesystem or pack files
  403.     len = FS_FOpenFile (path, &h);
  404.     if (!h)
  405.     {
  406.         if (buffer)
  407.             *buffer = NULL;
  408.         return -1;
  409.     }
  410.     
  411.     if (!buffer)
  412.     {
  413.         fclose (h);
  414.         return len;
  415.     }
  416.  
  417.     buf = Z_Malloc(len);
  418.     *buffer = buf;
  419.  
  420.     FS_Read (buf, len, h);
  421.  
  422.     fclose (h);
  423.  
  424.     return len;
  425. }
  426.  
  427.  
  428. /*
  429. =============
  430. FS_FreeFile
  431. =============
  432. */
  433. void FS_FreeFile (void *buffer)
  434. {
  435.     Z_Free (buffer);
  436. }
  437.  
  438. /*
  439. =================
  440. FS_LoadPackFile
  441.  
  442. Takes an explicit (not game tree related) path to a pak file.
  443.  
  444. Loads the header and directory, adding the files at the beginning
  445. of the list so they override previous pack files.
  446. =================
  447. */
  448. pack_t *FS_LoadPackFile (char *packfile)
  449. {
  450.     dpackheader_t    header;
  451.     int                i;
  452.     packfile_t        *newfiles;
  453.     int                numpackfiles;
  454.     pack_t            *pack;
  455.     FILE            *packhandle;
  456.     dpackfile_t        info[MAX_FILES_IN_PACK];
  457.     unsigned        checksum;
  458.  
  459.     packhandle = fopen(packfile, "rb");
  460.     if (!packhandle)
  461.         return NULL;
  462.  
  463.     fread (&header, 1, sizeof(header), packhandle);
  464.     if (LittleLong(header.ident) != IDPAKHEADER)
  465.         Com_Error (ERR_FATAL, "%s is not a packfile", packfile);
  466.     header.dirofs = LittleLong (header.dirofs);
  467.     header.dirlen = LittleLong (header.dirlen);
  468.  
  469.     numpackfiles = header.dirlen / sizeof(dpackfile_t);
  470.  
  471.     if (numpackfiles > MAX_FILES_IN_PACK)
  472.         Com_Error (ERR_FATAL, "%s has %i files", packfile, numpackfiles);
  473.  
  474.     newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t));
  475.  
  476.     fseek (packhandle, header.dirofs, SEEK_SET);
  477.     fread (info, 1, header.dirlen, packhandle);
  478.  
  479. // crc the directory to check for modifications
  480.     checksum = Com_BlockChecksum ((void *)info, header.dirlen);
  481.  
  482. #ifdef NO_ADDONS
  483.     if (checksum != PAK0_CHECKSUM)
  484.         return NULL;
  485. #endif
  486. // parse the directory
  487.     for (i=0 ; i<numpackfiles ; i++)
  488.     {
  489.         strcpy (newfiles[i].name, info[i].name);
  490.         newfiles[i].filepos = LittleLong(info[i].filepos);
  491.         newfiles[i].filelen = LittleLong(info[i].filelen);
  492.     }
  493.  
  494.     pack = Z_Malloc (sizeof (pack_t));
  495.     strcpy (pack->filename, packfile);
  496.     pack->handle = packhandle;
  497.     pack->numfiles = numpackfiles;
  498.     pack->files = newfiles;
  499.     
  500.     Com_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
  501.     return pack;
  502. }
  503.  
  504.  
  505. /*
  506. ================
  507. FS_AddGameDirectory
  508.  
  509. Sets fs_gamedir, adds the directory to the head of the path,
  510. then loads and adds pak1.pak pak2.pak ... 
  511. ================
  512. */
  513. void FS_AddGameDirectory (char *dir)
  514. {
  515.     int                i;
  516.     searchpath_t    *search;
  517.     pack_t            *pak;
  518.     char            pakfile[MAX_OSPATH];
  519.  
  520.     strcpy (fs_gamedir, dir);
  521.  
  522.     //
  523.     // add the directory to the search path
  524.     //
  525.     search = Z_Malloc (sizeof(searchpath_t));
  526.     strcpy (search->filename, dir);
  527.     search->next = fs_searchpaths;
  528.     fs_searchpaths = search;
  529.  
  530.     //
  531.     // add any pak files in the format pak0.pak pak1.pak, ...
  532.     //
  533.     for (i=0; i<10; i++)
  534.     {
  535.         Com_sprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", dir, i);
  536.         pak = FS_LoadPackFile (pakfile);
  537.         if (!pak)
  538.             continue;
  539.         search = Z_Malloc (sizeof(searchpath_t));
  540.         search->pack = pak;
  541.         search->next = fs_searchpaths;
  542.         fs_searchpaths = search;        
  543.     }
  544.  
  545.  
  546. }
  547.  
  548. /*
  549. ============
  550. FS_Gamedir
  551.  
  552. Called to find where to write a file (demos, savegames, etc)
  553. ============
  554. */
  555. char *FS_Gamedir (void)
  556. {
  557.     if (*fs_gamedir)
  558.         return fs_gamedir;
  559.     else
  560.         return BASEDIRNAME;
  561. }
  562.  
  563. /*
  564. =============
  565. FS_ExecAutoexec
  566. =============
  567. */
  568. void FS_ExecAutoexec (void)
  569. {
  570.     char *dir;
  571.     char name [MAX_QPATH];
  572.  
  573.     dir = Cvar_VariableString("gamedir");
  574.     if (*dir)
  575.         Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, dir); 
  576.     else
  577.         Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, BASEDIRNAME); 
  578.     if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM))
  579.         Cbuf_AddText ("exec autoexec.cfg\n");
  580.     Sys_FindClose();
  581. }
  582.  
  583.  
  584. /*
  585. ================
  586. FS_SetGamedir
  587.  
  588. Sets the gamedir and path to a different directory.
  589. ================
  590. */
  591. void FS_SetGamedir (char *dir)
  592. {
  593.     searchpath_t    *next;
  594.  
  595.     if (strstr(dir, "..") || strstr(dir, "/")
  596.         || strstr(dir, "\\") || strstr(dir, ":") )
  597.     {
  598.         Com_Printf ("Gamedir should be a single filename, not a path\n");
  599.         return;
  600.     }
  601.  
  602.     //
  603.     // free up any current game dir info
  604.     //
  605.     while (fs_searchpaths != fs_base_searchpaths)
  606.     {
  607.         if (fs_searchpaths->pack)
  608.         {
  609.             fclose (fs_searchpaths->pack->handle);
  610.             Z_Free (fs_searchpaths->pack->files);
  611.             Z_Free (fs_searchpaths->pack);
  612.         }
  613.         next = fs_searchpaths->next;
  614.         Z_Free (fs_searchpaths);
  615.         fs_searchpaths = next;
  616.     }
  617.  
  618.     //
  619.     // flush all data, so it will be forced to reload
  620.     //
  621.     if (dedicated && !dedicated->value)
  622.         Cbuf_AddText ("vid_restart\nsnd_restart\n");
  623.  
  624.     Com_sprintf (fs_gamedir, sizeof(fs_gamedir), "%s/%s", fs_basedir->string, dir);
  625.  
  626.     if (!strcmp(dir,BASEDIRNAME) || (*dir == 0))
  627.     {
  628.         Cvar_FullSet ("gamedir", "", CVAR_SERVERINFO|CVAR_NOSET);
  629.         Cvar_FullSet ("game", "", CVAR_LATCH|CVAR_SERVERINFO);
  630.     }
  631.     else
  632.     {
  633.         Cvar_FullSet ("gamedir", dir, CVAR_SERVERINFO|CVAR_NOSET);
  634.         if (fs_cddir->string[0])
  635.             FS_AddGameDirectory (va("%s/%s", fs_cddir->string, dir) );
  636.         FS_AddGameDirectory (va("%s/%s", fs_basedir->string, dir) );
  637.     }
  638. }
  639.  
  640.  
  641. /*
  642. ================
  643. FS_Link_f
  644.  
  645. Creates a filelink_t
  646. ================
  647. */
  648. void FS_Link_f (void)
  649. {
  650.     filelink_t    *l, **prev;
  651.  
  652.     if (Cmd_Argc() != 3)
  653.     {
  654.         Com_Printf ("USAGE: link <from> <to>\n");
  655.         return;
  656.     }
  657.  
  658.     // see if the link already exists
  659.     prev = &fs_links;
  660.     for (l=fs_links ; l ; l=l->next)
  661.     {
  662.         if (!strcmp (l->from, Cmd_Argv(1)))
  663.         {
  664.             Z_Free (l->to);
  665.             if (!strlen(Cmd_Argv(2)))
  666.             {    // delete it
  667.                 *prev = l->next;
  668.                 Z_Free (l->from);
  669.                 Z_Free (l);
  670.                 return;
  671.             }
  672.             l->to = CopyString (Cmd_Argv(2));
  673.             return;
  674.         }
  675.         prev = &l->next;
  676.     }
  677.  
  678.     // create a new link
  679.     l = Z_Malloc(sizeof(*l));
  680.     l->next = fs_links;
  681.     fs_links = l;
  682.     l->from = CopyString(Cmd_Argv(1));
  683.     l->fromlength = strlen(l->from);
  684.     l->to = CopyString(Cmd_Argv(2));
  685. }
  686.  
  687. /*
  688. ** FS_ListFiles
  689. */
  690. char **FS_ListFiles( char *findname, int *numfiles, unsigned musthave, unsigned canthave )
  691. {
  692.     char *s;
  693.     int nfiles = 0;
  694.     char **list = 0;
  695.  
  696.     s = Sys_FindFirst( findname, musthave, canthave );
  697.     while ( s )
  698.     {
  699.         if ( s[strlen(s)-1] != '.' )
  700.             nfiles++;
  701.         s = Sys_FindNext( musthave, canthave );
  702.     }
  703.     Sys_FindClose ();
  704.  
  705.     if ( !nfiles )
  706.         return NULL;
  707.  
  708.     nfiles++; // add space for a guard
  709.     *numfiles = nfiles;
  710.  
  711.     list = malloc( sizeof( char * ) * nfiles );
  712.     memset( list, 0, sizeof( char * ) * nfiles );
  713.  
  714.     s = Sys_FindFirst( findname, musthave, canthave );
  715.     nfiles = 0;
  716.     while ( s )
  717.     {
  718.         if ( s[strlen(s)-1] != '.' )
  719.         {
  720.             list[nfiles] = strdup( s );
  721. #ifdef _WIN32
  722.             strlwr( list[nfiles] );
  723. #endif
  724.             nfiles++;
  725.         }
  726.         s = Sys_FindNext( musthave, canthave );
  727.     }
  728.     Sys_FindClose ();
  729.  
  730.     return list;
  731. }
  732.  
  733. /*
  734. ** FS_Dir_f
  735. */
  736. void FS_Dir_f( void )
  737. {
  738.     char    *path = NULL;
  739.     char    findname[1024];
  740.     char    wildcard[1024] = "*.*";
  741.     char    **dirnames;
  742.     int        ndirs;
  743.  
  744.     if ( Cmd_Argc() != 1 )
  745.     {
  746.         strcpy( wildcard, Cmd_Argv( 1 ) );
  747.     }
  748.  
  749.     while ( ( path = FS_NextPath( path ) ) != NULL )
  750.     {
  751.         char *tmp = findname;
  752.  
  753.         Com_sprintf( findname, sizeof(findname), "%s/%s", path, wildcard );
  754.  
  755.         while ( *tmp != 0 )
  756.         {
  757.             if ( *tmp == '\\' ) 
  758.                 *tmp = '/';
  759.             tmp++;
  760.         }
  761.         Com_Printf( "Directory of %s\n", findname );
  762.         Com_Printf( "----\n" );
  763.  
  764.         if ( ( dirnames = FS_ListFiles( findname, &ndirs, 0, 0 ) ) != 0 )
  765.         {
  766.             int i;
  767.  
  768.             for ( i = 0; i < ndirs-1; i++ )
  769.             {
  770.                 if ( strrchr( dirnames[i], '/' ) )
  771.                     Com_Printf( "%s\n", strrchr( dirnames[i], '/' ) + 1 );
  772.                 else
  773.                     Com_Printf( "%s\n", dirnames[i] );
  774.  
  775.                 free( dirnames[i] );
  776.             }
  777.             free( dirnames );
  778.         }
  779.         Com_Printf( "\n" );
  780.     };
  781. }
  782.  
  783. /*
  784. ============
  785. FS_Path_f
  786.  
  787. ============
  788. */
  789. void FS_Path_f (void)
  790. {
  791.     searchpath_t    *s;
  792.     filelink_t        *l;
  793.  
  794.     Com_Printf ("Current search path:\n");
  795.     for (s=fs_searchpaths ; s ; s=s->next)
  796.     {
  797.         if (s == fs_base_searchpaths)
  798.             Com_Printf ("----------\n");
  799.         if (s->pack)
  800.             Com_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
  801.         else
  802.             Com_Printf ("%s\n", s->filename);
  803.     }
  804.  
  805.     Com_Printf ("\nLinks:\n");
  806.     for (l=fs_links ; l ; l=l->next)
  807.         Com_Printf ("%s : %s\n", l->from, l->to);
  808. }
  809.  
  810. /*
  811. ================
  812. FS_NextPath
  813.  
  814. Allows enumerating all of the directories in the search path
  815. ================
  816. */
  817. char *FS_NextPath (char *prevpath)
  818. {
  819.     searchpath_t    *s;
  820.     char            *prev;
  821.  
  822.     if (!prevpath)
  823.         return fs_gamedir;
  824.  
  825.     prev = fs_gamedir;
  826.     for (s=fs_searchpaths ; s ; s=s->next)
  827.     {
  828.         if (s->pack)
  829.             continue;
  830.         if (prevpath == prev)
  831.             return s->filename;
  832.         prev = s->filename;
  833.     }
  834.  
  835.     return NULL;
  836. }
  837.  
  838.  
  839. /*
  840. ================
  841. FS_InitFilesystem
  842. ================
  843. */
  844. void FS_InitFilesystem (void)
  845. {
  846.     Cmd_AddCommand ("path", FS_Path_f);
  847.     Cmd_AddCommand ("link", FS_Link_f);
  848.     Cmd_AddCommand ("dir", FS_Dir_f );
  849.  
  850.     //
  851.     // basedir <path>
  852.     // allows the game to run from outside the data tree
  853.     //
  854.     fs_basedir = Cvar_Get ("basedir", ".", CVAR_NOSET);
  855.  
  856.     //
  857.     // cddir <path>
  858.     // Logically concatenates the cddir after the basedir for 
  859.     // allows the game to run from outside the data tree
  860.     //
  861.     fs_cddir = Cvar_Get ("cddir", "", CVAR_NOSET);
  862.     if (fs_cddir->string[0])
  863.         FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_cddir->string) );
  864.  
  865.     //
  866.     // start up with baseq2 by default
  867.     //
  868.     FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_basedir->string) );
  869.  
  870.     // any set gamedirs will be freed up to here
  871.     fs_base_searchpaths = fs_searchpaths;
  872.  
  873.     // check for game override
  874.     fs_gamedirvar = Cvar_Get ("game", "", CVAR_LATCH|CVAR_SERVERINFO);
  875.     if (fs_gamedirvar->string[0])
  876.         FS_SetGamedir (fs_gamedirvar->string);
  877. }
  878.  
  879.  
  880.  
  881.